home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
MacWorld 1999 July
/
Macworld (1999-07).dmg
/
MPEG 3 Utilities
/
MacAMP 1.0b7
/
Visual Plugins Programming
/
LevelMeter++
/
Source
/
main.c
< prev
next >
Wrap
Text File
|
1999-01-30
|
10KB
|
417 lines
/*
LevelMeter Visual Plugin Example
Sample visual plugin to demonstrate MacAmp visual plugin API and additional tricks,
including access of the plugin resource fork, initializing the plugin info structure on
startup and more.
You can use any portions of this code freely in your plugins without any restrictions.
By: Slava Karpenko (slava@macamp.com) 9/2/98
@soft
*/
#include "visual.h"
// Some useful defines.
#define MIN(a,b) (((a)<(b))?(a):(b))
#define MAX(a,b) (((a)>(b))?(a):(b))
#define ABS(a) ((a)<0?-(a):(a))
// This is the gPlugInfo block - a place where MacAmp gets initial information about your plugin.
// Make sure to set the fragment's entry point to gPlugInfo to make it accessible for the MacAmp routines.
VPInfoBlock gPlugInfo =
{
VP_EXT_API_VERSION, // Set to VP_EXT_API_VERSION
plugVisual, // Should be always plugVisual (otherwise what are we doing here? =)
nil, // We have no flags
nil, // We have no global refcon.
"\pLevelMeter++", // What's our name to display in the Plugins menu?
// Function pointers. Use nil if you don't use something.
PlugInitialize,
PlugTerminate,
PlugProcess,
PlugMacEvent,
PlugKeyDown, // (unused in this example)
PlugIdle, // (unused in this example)
PlugSettings, // (unused in this example)
PlugAbout,
// • NEW FOR 1.2 •
PlugSongStart,
PlugSongEnd,
nil // MacAMP will fill out this value for us.
};
// These are globals.
static long ticks; // tick count variable to keep some sort of synch.
static short left; // left and right channel values (0<=x<=20)
static short right;
static short refnum; // plugin resource fork refnum
// dirty hack.
QDGlobalsPtr qdPtr = nil;
// Other function prototypes
void ProcessMP3(short chan, const double* stream);
void ProcessMP2(short chan, const double* stream);
void DrawMeters(WindowPtr wind);
OSErr PlugLoadMyself(void);
void PlugUnloadMyself(void);
/*
PlugLoadMyself
Called when your plugin fragment is loaded in memory. You may wish to change gPlugInfo fields here, or
do something else like opening your global preferences. Take a look at the PPC Linker panel in the target settings
to get an idea how it's done.
*/
OSErr PlugLoadMyself(void)
{
return noErr;
}
/*
PlugUnloadMyself
Called when MacAmp is about to quit and dispose all plugins. Dispose any global stuff you allocated in PlugLoadMyself
etc.
*/
void PlugUnloadMyself(void)
{
return;
}
/*
PlugInitialize
Called every time use selects your plugin from the Plugins menu. You need to allocate and display a window here,
init your variables, and do whatever you want, like set the refnum if you need it.
This function uses FSSpec to itself that MacAmp provides to it to gain access to its resource fork.
Other than that, everything is hard-coded and is definitly not an example of good programming =P
*/
OSErr PlugInitialize(WindowPtr* window, FSSpecPtr file, UInt32*)
{
Rect bounds = { 40, 10, 100, 19 }; // guess what this is, it's window coordinates. Yuck. =)
GrafPtr port;
GetPort(&port);
// create a new window with floating look as included in MacOS 7.6+ (WDEF id 124)
*window = NewCWindow(nil,&bounds,"\p",true,1985,(WindowPtr)-1,true,nil);
if (!*window)
return visNoMemory;
SetPort(*window);
BackColor(blackColor);
PenSize(4, 1);
SetPort(port);
// init our variables.
left = right = 0;
ticks = TickCount();
// draw zeroes initially
DrawMeters(*window);
// Open our resource fork and save the refnum for closing it (we are a well-behaved plugin, right? ;)
refnum = FSpOpenResFile(file, fsRdPerm);
// Get qd globals from the application by calling the callback
if (gPlugInfo.callbacks != nil)
qdPtr = gPlugInfo.callbacks->getQDPtrProc();
return visNoErr;
}
/*
PlugTerminate
Called when user deselects your plugin, MacAmp quits or in the case if you have passed visTerminate/visNoMemory error.
You should dispose your window here & clean up the mess.
*/
OSErr PlugTerminate(WindowPtr window, UInt32*)
{
if (window != nil)
DisposeWindow(window);
// Close our resource fork.
if (refnum != -1)
FSClose(refnum);
return visNoErr;
}
/*
PlugProcess
The heart of the visual plugin. It means your plugin needs to analyze the stream and display whatever you want.
chan is the number of channels.
stream is the sound data, which has the size of [2][32][dataSize]. For now dataSize==18 means layer III files,
dataSize == 36 means layer II files. Sound data is separated by channels, so stream[0] would be left, and stream[1] -
right channel.
You should never ever modify the contents of the stream pointer. Doing so will affect the playback.
[On the other hand, if you know what you're doing, you CAN modify the data stream. Doing so will change the sound.
So with a little knowledge you can turn a visual plugin into a sound plugin (make a new equalizer ;)]
*/
OSErr PlugProcess(WindowPtr window, short chan, const double* stream, short dataSize, UInt32*)
{
if (TickCount() - ticks > 1)
{
if (dataSize == 18)
ProcessMP3(chan, stream);
else
ProcessMP2(chan, stream);
DrawMeters(window);
left--;
right--;
ticks = TickCount();
}
return visNoErr;
}
/*
PlugMacEvent
Process a mac event that is related to the window (activate/deactivate, update, mouseDown).
You can do whatever you want, but remember that if you don't handle update events, that will screw up the whole
MacAmp window update process.
*/
OSErr PlugMacEvent(WindowPtr window, EventRecord* event, UInt32*)
{
OSErr result = visNoErr;
switch (event->what)
{
case updateEvt:
BeginUpdate(window);
DrawMeters(window);
EndUpdate(window);
break;
case mouseDown:
{
WindowPtr wind;
short thePart = FindWindow(event->where, &wind);
switch (thePart)
{
case inDrag:
// ugly hack. it shoulkd be qd.screenBits.bounds.
DragWindow(wind, event->where, &qdPtr->screenBits.bounds);
break;
case inGoAway:
if (TrackGoAway(wind, event->where))
result = visTerminate; // if user clicked the close box, shut ourself down.
break;
case inContent:
// hehe, max out everything
left = right = 20;
DrawMeters(wind);
break;
}
}
break;
default:
break;
}
return result;
}
/*
PlugAbout
Called when a user selects "About the Plugin" from the menu. You should display some sort of about box,
do some sort of an effect or whatever you feel like it.
*/
OSErr PlugAbout(WindowPtr, UInt32*)
{
NoteAlert(1234, nil);
return visNoErr;
}
/*
PlugIdle
Called whenever MacAmp gets some spare time. It's a good place to decrement your values, draw the stuff etc.
Called only if flagGetIdle is set.
*/
OSErr PlugIdle(WindowPtr window, UInt32* refcon)
{
#pragma unused (window, refcon)
return visNoErr;
}
/*
PlugKeyDown
Called when a user presses a button, and after it was processed by MacAmp. Keep in mind that no matter if it
was processed by MacAmp or not, your plug still gets it. So if you want tto have some keyboard control, try to
use unused keys.
Called only if flagGetKeyDown is set.
*/
OSErr PlugKeyDown(WindowPtr window, char key, short modifiers, UInt32* refcon)
{
#pragma unused (window, key, modifiers, refcon)
return visNoErr;
}
/*
PlugSettings
Called when user selects your plugin from the Settings submenu. Only used if you have declared that
you support settings (flagHasSettings is set).
You should display the settings dialog here, or do whateverr you feel like. =) Just remember that when user
chooses settings (s)he expects some interactino with the plugin, like the dialog or like that.
*/
OSErr PlugSettings(WindowPtr window, UInt32* refcon)
{
#pragma unused (window, refcon)
return visNoErr;
}
/*
PlugSongStart
Gets called immediately before the song start. You can obtain ID3 information about the track here etc
if you want -- it won't be changed until next song starts.
*/
OSErr PlugSongStart(WindowPtr, UInt32*)
{
// we don't do anything.
return visNoErr;
}
/*
PlugSongEnd
Gets called immediately after the song end. It is a good idea to clear/stop any animations at this point
because you will not receive PlugProcess calls until next song is started -- that can happen in one
minute/one second or never. You never know. =)
*/
OSErr PlugSongEnd(WindowPtr window, UInt32* refcon)
{
#pragma unused (window, refcon)
// just reset the bars and draw them.
left = 0;
right = 0;
DrawMeters(window);
return visNoErr;
}
#pragma mark -
// The following two functions are examples of analyzing the data stream.
void ProcessMP3(short chan, const double* stream)
{
int z;
double sum1 = 0.0;
double sum2 = 0.0;
if (chan == 2)
{
for (z = 1; z < 20; z ++)
sum1 += stream[z];
for (z = 1; z < 20; z ++)
sum2 += stream[32*18+z];
} else {
for (z = 1; z < 20; z ++)
sum1 += stream[z];
sum2 = sum1;
}
sum1 = ABS(sum1) * 20.0;
sum1 = MIN(20.0, MAX(0.0, sum1));
left = MAX(left, sum1);
sum2 = ABS(sum2) * 20.0;
sum2 = MIN(20.0, MAX(0.0, sum2));
right = MAX(right, sum2);
}
void ProcessMP2(short chan, const double* stream)
{
short z;
double sum1 = 0.0;
double sum2 = 0.0;
if (chan == 2)
{
for (z = 0; z < 20; z ++)
sum1 += stream[z];
for (z = 0; z < 20; z ++)
sum2 += stream[32*36+z];
}
else
{
for (z = 0; z < 20; z ++)
sum1 += stream[z];
sum2 = sum1;
}
sum1 = ABS(sum1) * 10;
sum1 = MIN(20.0, MAX(0.0, sum1));
left = MAX(left, sum1);
sum2 = ABS(sum2) * 10;
sum2 = MIN(20.0, MAX(0.0, sum2));
right = MAX(right, sum2);
}
// And this is a really simple function that draws the meter bars.
void DrawMeters(WindowPtr wind)
{
GrafPtr oldPort;
unsigned short pos;
GetPort(&oldPort);
SetPort(wind);
EraseRect(&wind->portRect);
ForeColor(greenColor);
MoveTo(0,60);
Line(0, -(left * 3));
MoveTo(5, 60);
Line(0, -(right * 3));
ForeColor(redColor);
// call the callback to find out current song position. If it returns an error, we are probably isn't
// playing a song. Just ignore it and set the position to 0. You can do more extended error checking,
// depending on your needs.
if (gPlugInfo.callbacks->getCurrentPosProc(&pos) != visNoErr)
pos = 0;
MoveTo(2, 0);
Line(0, pos * 0.6);
SetPort(oldPort);
}